#pragma once
#include <math.h>
/*
	This is a generic vector 3 class that can be used with almost 
	nuumber data type and has most of it operators being overloaded

*/

template <class T>
class Vector3
{
public:
	static const Vector3<T> ZERO;

	// constructors
	Vector3():
	_x(0),_y(0),_z(0){}

	Vector3(T x, T y, T z):
	_x(x),_y(y),_z(z){}
	// x y and z values
	T X();
	T Y();
	T Z();
	// set x y and z values
	void setX(T value);
	void setY(T value);
	void setZ(T value);

	// gest the magintued of the vector
	double Magnitude() const;
	// gets the magnitude sqared to avoid sqare root
	double MagnitudeSquared() const;
	// return the normal(unit vector) of the vector // dosent work with ints 
	Vector3<T> Normal() const;

	double AngleBetween(Vector3<T> value);
	static double AngleBetween(Vector3<T> v1, Vector3<T> v2)
	{return v1.AngleBetween(v2);}

	float Dot(Vector3<T> value) const ;
	static float Dot(Vector3<T> v1, Vector3<T> v2)
	{return v1.Dot(v2);}

	Vector3<float> Cross(Vector3<T> value) const;
	static Vector3<float> Cross(Vector3<T> v1, Vector3<T> v2)
	{return v1.Cross(v2);}


	// over load the operators
	Vector3<T> operator+ (Vector3<T> value);
	Vector3<T> operator- (Vector3<T> value);
	Vector3<T> operator* (float scaler);
	Vector3<T> operator+= (Vector3<T> value);
	Vector3<T> operator-= (Vector3<T> value);
	Vector3<T> operator*= (float scaler);

	bool operator== (Vector3<T> value);

private:
	// actual values
	T _x;
	T _y;
	T _z;
};

template <class T>
const Vector3<T> Vector3<T>::ZERO = Vector3<T>(0,0,0);



/////////////////////// Dot Products///////////////////////////////
template <class T>
float Vector3<T>::Dot(Vector3<T> value) const
{
	float dot = (value._x * this->_x) + (value._y * this->_y) + (value._z * this->_z);
	return dot;
}

//////////////////////// end////////////////////////////

/////////////////////// Cross Products///////////////////////////////

template <class T>
Vector3<float> Vector3<T>::Cross(Vector3<T> value) const
{
	Vector3<float> cross = Vector3<float>::ZERO;

	if (value.MagnitudeSquared() < 0.001 || MagnitudeSquared() < 0.001)
		return cross;

	cross._x = (value._y * this->_z) - (value._z * this->_y);
	cross._y = (value._z * this->_x) - (value._x * this->_z);
	cross._z = (value._x * this->_y) - (value._y * this->_x);

	return cross;
}

/////////////////////////// End ////////////////////////////

/////////////////////// Angle Between///////////////////////////////

template <class T>
double Vector3<T>::AngleBetween(Vector3<T> value)
{
	double dot = this->Dot(value);
	double divisor = this->Magnitude() * value.Magnitude();
	if(divisor == 0)
	{
		return 0;
	}
	double ratio = dot/divisor;
	if(ratio >= 1 || ratio <= -1)
	{
		return 0.54030230586;
	}
	return acos(ratio);
}

/////////////////////////// End ////////////////////////////

template <class T>
Vector3<T> Vector3<T>::Normal() const
{
	Vector3<T> norm(this->_x, this->_y, this->_z); 
	if(MagnitudeSquared() < 0.001)
	{
		return Vector3<T>::ZERO;
	}
	norm *= (T)(1/this->Magnitude());
	return norm;
}

// actual magnitude
template <class T>
double Vector3<T>::Magnitude() const
{
	double squared = pow(_x,2) + pow(_y,2) + pow(_z,2);
	return sqrt(squared);
}
// magnitude  squared
template <class T>
double Vector3<T>::MagnitudeSquared() const
{
	return pow(_x,2) + pow(_y,2) + pow(_z,2);
}

// +
template <class T>
Vector3<T> Vector3<T>::operator+(Vector3<T> value)
{
	Vector3<T> sum(this->_x + value._x, this->_y + value._y, this->_z + value._z); 
	return sum;
}

// -
template <class T>
Vector3<T> Vector3<T>::operator-(Vector3<T> value)
{
	Vector3<T> sum(this->_x - value._x, this->_y - value._y, this->_z - value._z); 
	return sum;
}
// *
template <class T>
Vector3<T> Vector3<T>::operator*(float scaler)
{
	Vector3<T> sum((T)(this->_x * scaler), (T)(this->_y * scaler), (T)(this->_z * scaler)); 
	return sum;
}
// +=
template <class T>
Vector3<T> Vector3<T>::operator+=(Vector3<T> value)
{
	*this = *this + value;
	return *this;
}

// -=
template <class T>
Vector3<T> Vector3<T>::operator-=(Vector3<T> value)
{
	*this = *this - value;
	return *this;
}
// *
template <class T>
Vector3<T> Vector3<T>::operator*=(float scaler)
{
	*this = *this * scaler; 
	return *this;
}

template <class T>
bool Vector3<T>::operator==(Vector3<T> value)
{
	return this->_x == value._x && this->_y == value._y && this->_z == value._z;
}


template <class T>
T Vector3<T>::X()
{ return _x; }

template <class T>
T Vector3<T>::Y()
{return _y;}

template <class T>
T Vector3<T>::Z()
{return _z;}

template <class T>
void Vector3<T>::setX(T value)
{_x = value;}

template <class T>
void Vector3<T>::setY(T value)
{_y = value;}

template <class T>
void Vector3<T>::setZ(T value)
{_z = value;}
